﻿// -----------------------------------------------------------------------
// <copyright file="TextureProvider.cs" company="Microsoft">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace SpaceshipDuel
{
    using System;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Content;
    using Microsoft.Xna.Framework.Graphics;

    /// <summary>
    /// Struktur zur Speicherung der Textur und Zusatzdaten
    /// </summary>
    public struct TextureData
    {
        public Texture2D texture;   // Textur
        public int width;           // Breite in Pixel
        public int height;          // Höhe in Pixel
        public int halfwidth;       // halbe Breite (für Mittelpunkt)
        public int halfheight;      // halbe Höhe (für Mittelpunkt)
        public int tileCountX;      // Anzahl Tiles waagerecht
        public int tileCountY;      // Anzahl Tiles senkrecht
        public int tileWidth;       // Breite eines Tiles
        public int tileHeight;      // Höhe eines Tiles
    }

    /// <summary>
    /// Verwaltet Texturen und Texturdaten inklusive Colorarrays
    /// </summary>
    public static class TextureProvider
    {
        #region Private fields

        static Random rnd; // Zufallszahlengenerator für Generierung des Backgrounds

        // Texturen
        static RenderTarget2D bgBuffer;        // 2048er Background wird aus Hintergrundkacheln gerendert
        static TextureData backgroundTiles;    // Textur mit Kacheln für Hintergrund
        static TextureData shipPlayer1;        // Textur Raumschiff Spieler 1 (links)
        static TextureData shipPlayer2;        // Textur Raumschiff Spieler 2 (rechts)
        static TextureData shieldPlayer1;      // Textur Schutzschild Raumschiff Spieler 1
        static TextureData shieldPlayer2;      // Textur Schutzschild Raumschiff Spieler 2
        static TextureData explosion;          // Textur Explosion, 64 Tiles
        static TextureData bulletPlayer1;      // Textur Schuss Spieler 1
        static TextureData bulletPlayer2;      // Textur Schuss Spieler 2
        static TextureData bulletEnemy;        // Textur Schüsse Feinde
        static TextureData pixel;              // Textur für HUD (Radar, Trennlinie)
        static TextureData upgrades;           // Textur für Upgrades-Symbole
        static TextureData radarBackground;    // Textur für Radar Hintergrund
        static TextureData asteroidBig;        // Textur für Asteroid
        static TextureData asteroidMedium;     // Textur für Asteroid mittel
        static TextureData asteroidSmall;      // Textur für Asteroid klein
        static TextureData mine;               // Textur für Mine
        static TextureData winTextGerman;      // Textur Sieg
        static TextureData looseTextGerman;    // Textur Niederlage
        static TextureData tieTextGerman;      // Textur Unentschieden
        static TextureData winTextEnglish;     // Textur victory
        static TextureData looseTextEnglish;   // Textur defeat
        static TextureData tieTextEnglish;     // Textur tie
        static TextureData pauseTextGerman;    // Textur Pause
        static TextureData warpPlayer1;        // Warpanimation Spieler1
        static TextureData warpPlayer2;        // Warpanimation Spieler2
        static TextureData particle;           // Partikel, rund
        static TextureData title;              // Textur für Titelbildschirm
        static TextureData titlesmall;         // Textur für Titelbildschirm
        static TextureData turret;             // Textur für Turm
        static TextureData alien1;             // Textur für Alien-Raumschiff 1
        static TextureData alien2;             // Textur für Alien-Raumschiff 2
        static TextureData alien3;             // Textur für Alien-Raumschiff 3
        static TextureData alien4;             // Textur für Alien-Raumschiff 4
        static TextureData atomrocket;         // Textur für Atomrakete (Spezialwaffe)
        static TextureData shockwave;          // Textur für Explosion Atomrakete
        static TextureData shieldHud;          // Textur für Schild-HUD-Anzeige
        static TextureData radarShip;          // Textur für Radaranzeige Spielerschiff

        // Colormaps für pixelgenaue Kollisionserkennung
        static Color[,] player1Colors;             // Colormap Schiff 1
        static Color[,] player2Colors;             // Colormap Schiff 2
        static Color[,] upgradesColors;            // Colormap Upgrade
        static Color[,] asteroidBigColors;         // Colormap Asteroid groß
        static Color[,] asteroidMediumColors;      // Colormap Asteroid mittel
        static Color[,] asteroidSmallColors;       // Colormap Asteroid klein
        static Color[,] bulletColors;              // Colormap Geschoss
        static Color[,] mineColors;                // Colormap Mine   
        static Color[,] alien1Colors;              // Colormap Alienschiff 1      
        static Color[,] alien2Colors;              // Colormap Alienschiff 2
        static Color[,] alien3Colors;              // Colormap Alienschiff 3
        static Color[,] alien4Colors;              // Colormap Alienschiff 4
        static Color[,] atomrocketColors;          // Colormap für Atomrakete

        #endregion Private fields

        #region Public fields

        #endregion

        #region Construction

        /// <summary>
        /// Standart-Konstruktor ist private, weil die Klasse Singleton sein soll
        /// </summary>
        static TextureProvider() 
        {
            rnd = new Random(); // Initialisierung des Zufallszahlengenerators
        }

        #endregion Construction

        #region Public properties

        public static ContentManager Content { get; set; }

        public static GraphicsDevice GfxDev { get; set; }

        public static Texture2D TexBackground           
        { 
            get 
            {
                if (bgBuffer.IsContentLost == true)
                    CreateBackgroundBufferTexture();
                return (Texture2D) bgBuffer; 
            } 
        }

        public static TextureData TexShipPlayer1        { get { return shipPlayer1; } }
        public static TextureData TexShipPlayer2        { get { return shipPlayer2; } }
        public static TextureData TexShieldPlayer1      { get { return shieldPlayer1; } }
        public static TextureData TexShieldPlayer2      { get { return shieldPlayer2; } }
        public static TextureData TexExplosion          { get { return explosion; } }
        public static TextureData TexBulletPlayer1      { get { return bulletPlayer1; } }
        public static TextureData TexBulletPlayer2      { get { return bulletPlayer2; } }
        public static TextureData TexBulletEnemy        { get { return bulletEnemy; } }
        public static TextureData TexPixel              { get { return pixel; } }
        public static TextureData TexUpgrades           { get { return upgrades; } }
        public static TextureData TexRadarBackground    { get { return radarBackground; } }
        public static TextureData TexAsteroidBig        { get { return asteroidBig; } }
        public static TextureData TexAsteroidMedium     { get { return asteroidMedium; } }
        public static TextureData TexAsteroidSmall      { get { return asteroidSmall; } }
        public static TextureData TexMine               { get { return mine; } }
        public static TextureData TexWinTextGerman      { get { return winTextGerman; } }
        public static TextureData TexLooseTextGerman    { get { return looseTextGerman; } }
        public static TextureData TexTieTextGerman      { get { return tieTextGerman; } }
        public static TextureData TexWinTextEnglish     { get { return winTextEnglish; } }
        public static TextureData TexLooseTextEnglish   { get { return looseTextEnglish; } }
        public static TextureData TexTieTextEnglish     { get { return tieTextEnglish; } }
        public static TextureData TexPauseTextGerman    { get { return pauseTextGerman; } }
        public static TextureData TexWarpPlayer1        { get { return warpPlayer1; } }
        public static TextureData TexWarpPlayer2        { get { return warpPlayer2; } }
        public static TextureData TexParticle           { get { return particle; } }
        public static TextureData TexTitle              { get { return title; } }
        public static TextureData TexTitleSmall         { get { return titlesmall; } }
        public static TextureData TexTurret             { get { return turret; } }
        public static TextureData TexAlien1             { get { return alien1; } }
        public static TextureData TexAlien2             { get { return alien2; } }
        public static TextureData TexAlien3             { get { return alien3; } }
        public static TextureData TexAlien4             { get { return alien4; } }
        public static TextureData TexAtomRocket         { get { return atomrocket; } }
        public static TextureData TexShockwave          { get { return shockwave; } }
        public static TextureData TexShieldHud          { get { return shieldHud; } }
        public static TextureData TexRadarShip          { get { return radarShip; } }

        public static Color[,] ColPlayer1           { get { return player1Colors; } }
        public static Color[,] ColPlayer2           { get { return player2Colors; } }
        public static Color[,] ColUpgrades          { get { return upgradesColors; } }
        public static Color[,] ColAsteroidBig       { get { return asteroidBigColors; } }
        public static Color[,] ColAsteroidMedium    { get { return asteroidMediumColors; } }
        public static Color[,] ColAsteroidSmall     { get { return asteroidSmallColors; } }
        public static Color[,] ColBullet            { get { return bulletColors; } }
        public static Color[,] ColMine              { get { return mineColors; } }
        public static Color[,] ColAlien1            { get { return alien1Colors; } }
        public static Color[,] ColAlien2            { get { return alien2Colors; } }
        public static Color[,] ColAlien3            { get { return alien3Colors; } }
        public static Color[,] ColAlien4            { get { return alien4Colors; } }
        public static Color[,] ColAtomRocket        { get { return atomrocketColors; } }

        #endregion Public properties

        #region Public methods

        /// <summary>
        /// Initialisierung aller Daten
        /// </summary>
        public static void InitializeData()
        {
            LoadTextures();
            CreateBackgroundBufferTexture();
            CreateColorMaps();
        }

        public static void OnExit()
        {
            bgBuffer.Dispose();
            Content.Unload();
        }

        #endregion Public methods

        #region Private methods

        /// <summary>
        /// Texturen laden
        /// </summary>
        private static void LoadTextures()
        {
            LoadTexture(ref backgroundTiles,    @"Textures/background", 10, 10);    // Background = 100 Tiles zu je 64x64 Pixel
            LoadTexture(ref shipPlayer1,        @"Textures/player1ship");           // Schiff Spieler 1 64x64
            LoadTexture(ref shipPlayer2,        @"Textures/player2ship");           // Schiff Spieler 2 64x64
            LoadTexture(ref shieldPlayer1,      @"Textures/shield1");               // Schild Spieler 1 64x64
            LoadTexture(ref shieldPlayer2,      @"Textures/shield2");               // Schild Spieler 2 64x64
            LoadTexture(ref explosion,          @"Textures/explosion64", 8, 8);     // Explosion = 64 Tiles zu 128x128 Pixel
            LoadTexture(ref bulletPlayer1,      @"Textures/shot1");                 // Geschoss Spieler 1
            LoadTexture(ref bulletPlayer2,      @"Textures/shot2");                 // Geschoss Spieler 2
            LoadTexture(ref bulletEnemy,        @"Textures/shot3");                 // Geschoss Gegner
            LoadTexture(ref pixel,              @"Textures/pixel");                 // Pixeltextur für HUD (Radar, Trennlinie)
            LoadTexture(ref upgrades,           @"Textures/upgradeset", 4);         // Standard-Upgrades (3 Tiles)
            LoadTexture(ref radarBackground,    @"Textures/radar");                 // Radar (Kreis)
            LoadTexture(ref asteroidBig,        @"Textures/asteroid1");             // Asteroid groß
            LoadTexture(ref asteroidMedium,     @"Textures/asteroid1_klein");       // Asteroid mittel
            LoadTexture(ref asteroidSmall,      @"Textures/asteroid1_sehrklein");   // Asteroid klein
            LoadTexture(ref mine,               @"Textures/mine", 10, 5);           // Tiles für Mine (10x5 Tiles)
            LoadTexture(ref winTextGerman,      @"Textures/text_sieg");             // Text "Sieg"
            LoadTexture(ref looseTextGerman,    @"Textures/text_niederlage");       // Text "Niederlage"
            LoadTexture(ref tieTextGerman,      @"Textures/text_unentschieden");    // Text "Unentschieden"
            LoadTexture(ref winTextEnglish,     @"Textures/text_victory");          // Text "Victory"
            LoadTexture(ref looseTextEnglish,   @"Textures/text_defeat");           // Text "Defeat"
            LoadTexture(ref tieTextEnglish,     @"Textures/text_tie");              // Text "Tie"
            LoadTexture(ref pauseTextGerman,    @"Textures/text_pause");            // Text "Pause"
            LoadTexture(ref warpPlayer1,        @"Textures/player1warp", 16, 2);    // Warpanimation Spieler 1, 20 Tiles
            LoadTexture(ref warpPlayer2,        @"Textures/player2warp", 16, 2);    // Warpanimation Spieler 2, 20 Tiles
            LoadTexture(ref particle,           @"Textures/particle");              // Partikel
            LoadTexture(ref title,              @"Textures/titel");                 // Titelbildschirm
            LoadTexture(ref titlesmall,         @"Textures/titel_klein");           // Titelbildschirm
            LoadTexture(ref turret,             @"Textures/turm", 10);              // Turm
            LoadTexture(ref alien1,             @"Textures/alien1");                // Alien-Raumschiff 1
            LoadTexture(ref alien2,             @"Textures/alien2");                // Alien-Raumschiff 2
            LoadTexture(ref alien3,             @"Textures/alien3");                // Alien-Raumschiff 3
            LoadTexture(ref alien4,             @"Textures/alien4");                // Alien-Raumschiff 4
            LoadTexture(ref atomrocket,         @"Textures/atomrocket");            // Atomrakete
            LoadTexture(ref shockwave,          @"Textures/shockwave");             // Atomrakete Explosion
            LoadTexture(ref shieldHud,          @"Textures/shieldHud", 10, 3);      // Schild HUD
            LoadTexture(ref radarShip,          @"Textures/radar_ship");            // Radaranzeige Spielerschiff
        }

        /// <summary>
        /// Initialisiert eine TextureData-Struktur
        /// </summary>
        /// <param name="texdat">Referenz auf TextureData-Struktur</param>
        /// <param name="assetName">Pfad zur Textur-Ressource</param>
        /// <param name="tileCountX">Anzahl Tiles waagerecht (default=1)</param>
        /// <param name="tileCountY">Anzahl Tiles senkrecht (default=1)</param>
        private static void LoadTexture(ref TextureData texdat, string assetName, int tileCountX = 1, int tileCountY = 1)
        {
            texdat.texture      = Content.Load<Texture2D>(assetName);
            texdat.width        = texdat.texture.Width;
            texdat.height       = texdat.texture.Height;
            texdat.halfwidth    = texdat.texture.Width / 2;
            texdat.halfheight   = texdat.texture.Height / 2;
            texdat.tileCountX   = tileCountX;
            texdat.tileCountY   = tileCountY;
            texdat.tileWidth    = texdat.width / texdat.tileCountX;
            texdat.tileHeight   = texdat.height / texdat.tileCountY;
        }

        /// <summary>
        /// Erzeugt die Backgroundbuffer-Textur (2048x2048) aus den Backgroundtiles
        /// Dies ermöglicht einen dramatischen Performancegewinn gegenüber dem direkten Rendern der 64x64 Tiles, da wesentlich weniger
        /// Polygone und Draw-Operationen durchgeführt werden müssen, außerdem kann so das Spielfeld wesentlich größer dimensioniert werden
        /// </summary>
        private static void CreateBackgroundBufferTexture()
        {
            bgBuffer = new RenderTarget2D(GfxDev, 2048, 2048);
            GfxDev.SetRenderTarget(bgBuffer);
            GfxDev.Clear(Color.DarkBlue);

            int tilesize = backgroundTiles.width / backgroundTiles.tileCountX;
            int max = 2048 / tilesize;   // Anzahl der Tiles (Breite, Höhe)
            int tileX, tileY;

            SpriteBatch spriteBatch = new SpriteBatch(GfxDev);
            spriteBatch.Begin();

            for (int x = 0; x < max; x++)
            {
                for (int y = 0; y < max; y++)
                {
                    tileX = rnd.Next(backgroundTiles.tileCountX) * tilesize;
                    tileY = rnd.Next(backgroundTiles.tileCountY) * tilesize;

                    Rectangle destRect = new Rectangle(x * tilesize, y * tilesize, tilesize, tilesize);
                    Rectangle sourceRect = new Rectangle(tileX, tileY, tilesize, tilesize);

                    spriteBatch.Draw(backgroundTiles.texture, destRect, sourceRect, Color.White);
                }
            }

            spriteBatch.End();
            GfxDev.SetRenderTarget(null);
        }

        /// <summary>
        /// Erzeugt Colormaps für pixelgenaue Kollision aus den Texturen
        /// </summary>
        private static void CreateColorMaps()
        {
            // Color arrays for pixel perfect collision
            player1Colors = TextureToColorArray(shipPlayer1.texture);
            player2Colors = TextureToColorArray(shipPlayer2.texture);
            asteroidBigColors = TextureToColorArray(asteroidBig.texture);
            asteroidMediumColors = TextureToColorArray(asteroidMedium.texture);
            asteroidSmallColors = TextureToColorArray(asteroidSmall.texture);
            bulletColors = TextureToColorArray(bulletPlayer1.texture);
            upgradesColors = TextureToColorArray(upgrades.texture, upgrades.width / upgrades.tileCountX);
            mineColors = TextureToColorArray(mine.texture, mine.width / mine.tileCountX);
            alien1Colors = TextureToColorArray(alien1.texture);
            alien2Colors = TextureToColorArray(alien2.texture);
            alien3Colors = TextureToColorArray(alien3.texture);
            alien4Colors = TextureToColorArray(alien4.texture);
            atomrocketColors = TextureToColorArray(atomrocket.texture);
        }

        /// <summary>
        /// Umwandlung Texture -> 2D Color array
        /// Vorlage von hier: http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2D/Coll_Detection_Overview.php
        /// </summary>
        /// <param name="texture">Umzuwnadelnde Textur</param>
        /// <returns>2D Color Array</returns>
        private static Color[,] TextureToColorArray(Texture2D texture)
        {
            Color[] colorArray = new Color[texture.Width * texture.Height];
            texture.GetData(colorArray);

            Color[,] colorArrayReturn = new Color[texture.Width, texture.Height];
            for (int x = 0; x < texture.Width; x++)
            {
                for (int y = 0; y < texture.Height; y++)
                {
                    colorArrayReturn[x, y] = colorArray[x + y * texture.Width];
                }
            }
            return colorArrayReturn;
        }

        /// <summary>
        /// Umwandlung Texture -> 2D Color array für Textur mit mehreren (identischen) Tiles,
        /// z.B. bei den Upgrades
        /// </summary>
        /// <param name="texture">Textur</param>
        /// <param name="width">Breite/Höhe Tile</param>
        /// <returns>Colorarray</returns>
        private static Color[,] TextureToColorArray(Texture2D texture, int width)
        {
            Color[] colorArray = new Color[texture.Width * texture.Height];
            texture.GetData(colorArray);

            Color[,] colorArrayReturn = new Color[width, width];

            for (int x = 0; x < texture.Width; x++)
            {
                for (int y = 0; y < texture.Height; y++)
                {
                    if (x < width && y < width)
                        colorArrayReturn[x, y] = colorArray[x + y * texture.Width];
                }
            }

            return colorArrayReturn;
        }

        #endregion Private methods
    }
}
